#pragma once
namespace CPU {

template<typename T>
inline void gemm(T* A,  T* B, T* C, int m, int n, int k, int lda, int ldb, int ldc, T alpha,  T beta) {
    if constexpr (std::is_same_v<T, float>) {
        cblas_sgemm (CblasColMajor, CblasNoTrans, CblasNoTrans, m, n, k, alpha, A, lda, B, ldb, beta, C, ldc);
    } else if constexpr (std::is_same_v<T, double>) {
        cblas_dgemm (CblasColMajor, CblasNoTrans, CblasNoTrans, m, n, k, alpha, A, lda, B, ldb, beta, C, ldc);
    } else {
        // Handle other types or provide an error message
        static_assert(std::is_same_v<T, float> || std::is_same_v<T, double> , "Unsupported type");
    }
}


template<typename T>
inline void trmm(char _side, char _uplo, char _diag, const T* A, T* B, int m, int n, int lda, int ldb, T alpha) {
    assert(_side == 'L' || _side == 'R');
    assert(_uplo == 'L' || _uplo == 'U');
    assert(_diag == 'U' || _diag == 'N');
    CBLAS_SIDE side = _side == 'L' ? CblasLeft : CblasRight;
    CBLAS_UPLO uplo = _uplo == 'L' ? CblasLower : CblasUpper;
    CBLAS_DIAG diag = _diag == 'U' ? CblasUnit : CblasNonUnit;
    if constexpr (std::is_same_v<T, float>) {
        cblas_strmm (CblasColMajor, side, uplo, CblasNoTrans, diag, m, n, alpha, A, lda, B, ldb);
    } else if constexpr (std::is_same_v<T, double>) {
        cblas_dtrmm (CblasColMajor, side, uplo, CblasNoTrans, diag, m, n, alpha, A, lda, B, ldb);
    } else {
        // Handle other types or provide an error message
        static_assert(std::is_same_v<T, float> || std::is_same_v<T, double> , "Unsupported type");
    }
}

template<typename T>
inline void trsm(char _side, char _uplo, char _diag, const T* A, T* B, int m, int n, int lda, int ldb) {
    assert(_side == 'L' || _side == 'R');
    assert(_uplo == 'L' || _uplo == 'U');
    assert(_diag == 'U' || _diag == 'N');
    CBLAS_SIDE side = _side == 'L' ? CblasLeft : CblasRight;
    CBLAS_UPLO uplo = _uplo == 'L' ? CblasLower : CblasUpper;
    CBLAS_DIAG diag = _diag == 'U' ? CblasUnit : CblasNonUnit;
    const T alpha = 1.0;
    if constexpr (std::is_same_v<T, float>) {
        cblas_strsm (CblasColMajor, side, uplo, CblasNoTrans, diag, m, n, alpha, A, lda, B, ldb);
    } else if constexpr (std::is_same_v<T, double>) {
        cblas_dtrsm (CblasColMajor, side, uplo, CblasNoTrans, diag, m, n, alpha, A, lda, B, ldb);
    } else {
        // Handle other types or provide an error message
        static_assert(std::is_same_v<T, float> || std::is_same_v<T, double> , "Unsupported type");
    }
}

template<typename T>
inline void getrf(T* A, lapack_int * ipiv, int m, int n, int lda) {
    lapack_int ret = 0;
    if constexpr (std::is_same_v<T, float>) {
        ret = LAPACKE_sgetrf (LAPACK_COL_MAJOR, m , n , A , lda , ipiv );
    } else if constexpr (std::is_same_v<T, double>) {
        ret = LAPACKE_dgetrf (LAPACK_COL_MAJOR, m , n , A , lda , ipiv );
    } else {
        // Handle other types or provide an error message
        static_assert(std::is_same_v<T, float> || std::is_same_v<T, double> , "Unsupported type");
    }
    assert(ret == 0);
}


template<typename T>
inline void getrs(const T* A, const lapack_int *ipiv, T* B, int n, int nrhs, int lda, int ldb) {
    lapack_int ret = 0;
    if constexpr (std::is_same_v<T, float>) {
        ret =  LAPACKE_sgetrs (LAPACK_COL_MAJOR , 'N' , n , nrhs , A , lda , ipiv , B , ldb );
    } else if constexpr (std::is_same_v<T, double>) {
        ret =  LAPACKE_dgetrs (LAPACK_COL_MAJOR , 'N' , n , nrhs , A , lda , ipiv , B , ldb );
    } else {
        // Handle other types or provide an error message
        static_assert(std::is_same_v<T, float> || std::is_same_v<T, double> , "Unsupported type");
    }
    assert(ret == 0);
}

template<typename T> 
inline void getri(T* A, const lapack_int *ipiv, int n, int lda) {
    lapack_int ret = 0;
    if constexpr (std::is_same_v<T, float>) {
        ret =  LAPACKE_sgetri (LAPACK_COL_MAJOR , n , A , lda , ipiv );
    } else if constexpr (std::is_same_v<T, double>) {
        ret =  LAPACKE_dgetri (LAPACK_COL_MAJOR , n , A , lda , ipiv );
    } else {
        // Handle other types or provide an error message
        static_assert(std::is_same_v<T, float> || std::is_same_v<T, double> , "Unsupported type");
    }
    assert(ret == 0);
}

template<typename T>
inline void trtri(char uplo, char diag, T* A, int n, int lda) {
    lapack_int ret = 0;
    if constexpr (std::is_same_v<T, float>) {
        ret = LAPACKE_strtri (LAPACK_COL_MAJOR , uplo , diag , n , A , lda );
    } else if constexpr (std::is_same_v<T, double>) {
        ret = LAPACKE_dtrtri (LAPACK_COL_MAJOR , uplo , diag , n , A , lda );
    } else {
        // Handle other types or provide an error message
        static_assert(std::is_same_v<T, float> || std::is_same_v<T, double> , "Unsupported type");
    }
    assert(ret == 0);
}


template<typename T>
inline void gemv(T* result, const T* matrix, const T* vec, int m, int n, int lda, T alpha, T beta) {
    if constexpr (std::is_same_v<T, float>) {
        cblas_sgemv (CblasColMajor, CblasNoTrans, m, n, alpha, matrix, lda, vec, 1, beta, result, 1);
    } else if constexpr (std::is_same_v<T, double>) {
        cblas_dgemv (CblasColMajor, CblasNoTrans, m, n, alpha, matrix, lda, vec, 1, beta, result, 1);
    } else {
        // Handle other types or provide an error message
        static_assert(std::is_same_v<T, float> || std::is_same_v<T, double> , "Unsupported type");
    }
}

template<typename T>
inline void trmv(char _uplo, char _diag, const T* matrix, T* vec, int n, int lda) {
    assert(_uplo == 'L' || _uplo == 'U');
    assert(_diag == 'U' || _diag == 'N');
    CBLAS_UPLO uplo = _uplo == 'L' ? CblasLower : CblasUpper;
    CBLAS_DIAG diag = _diag == 'U' ? CblasUnit : CblasNonUnit;
    if constexpr (std::is_same_v<T, float>) {
        cblas_strmv (CblasColMajor, uplo, CblasNoTrans, diag, n, matrix, lda, vec, 1);
    } else if constexpr (std::is_same_v<T, double>) {
        cblas_dtrmv (CblasColMajor, uplo, CblasNoTrans, diag, n, matrix, lda, vec, 1);
    } else {
        // Handle other types or provide an error message
        static_assert(std::is_same_v<T, float> || std::is_same_v<T, double> , "Unsupported type");
    }
}

};